﻿#region MIT Licenze
//The MIT License

//Copyright (c) 2008 Sebastian Faltoni, dotnetfireball.org

//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:

//The above copyright notice and this permission notice shall be included in
//all copies or substantial portions of the Software.

//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//THE SOFTWARE.
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using Microsoft.Office.DocumentFormat.OpenXml.Packaging;
using System.IO;
using System.Windows.Media.Imaging;
using Fireball.Extensions;
using System.Windows.Markup;
using System.Xml;
using Fireball.OpenXML.Properties;

namespace Fireball.OpenXML.Word
{
    /// <summary>
    /// The implementation of The WordprocessingML
    /// </summary>
    [ContentProperty("Blocks")]
    public class WordDocument : OpenXMLEntity, IDisposable, IAddChild,IFindable
    {
        private WordprocessingDocument _WordDocument = null;
        private MainDocumentPart _MainDocumentPart = null;
        private Uri _FileName = null;

        /// <summary>
        /// The current file URI
        /// </summary>
        public Uri FileName
        {
            get
            {
                return _FileName;
            }
        }

        public WordDocument()
        {
            _Blocks = new BlockCollection(this);

            Guid rndName = Guid.NewGuid();

            var tmpPath = Path.GetTempPath();

            tmpPath = Path.Combine(tmpPath, rndName.ToString().Replace("-", "") + ".docx");

            _FileName = new Uri(tmpPath);

            using (Stream strm = typeof(WordDocument).Assembly.GetManifestResourceStream("Fireball.OpenXML.Word.template.docx"))
            {
                var allBts = strm.GetAllBytes();

                File.WriteAllBytes(tmpPath, allBts);
            }

            _WordDocument = WordprocessingDocument.Open(tmpPath, true);
            _MainDocumentPart = _WordDocument.MainDocumentPart;
        }

        public WordDocument(Uri path)
        {
            _Blocks = new BlockCollection(this);

            _FileName = path;
            using (Stream strm = typeof(WordDocument).Assembly.GetManifestResourceStream("Fireball.OpenXML.Word.template.docx"))
            {
                var allBts = strm.GetAllBytes();

                File.WriteAllBytes(path.LocalPath, allBts);
            }

            _WordDocument = WordprocessingDocument.Open(path.LocalPath, true);
            _MainDocumentPart = _WordDocument.MainDocumentPart;
        }


        private BlockCollection _Blocks = null;

        //public static readonly XNamespace WordNameSpace = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";
        //public static readonly XNamespace RelationShipNameSpace = "http://schemas.openxmlformats.org/officeDocument/2006/relationships";
        //public static readonly XNamespace WordprocessingDrawing = "http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing";

        /// <summary>
        /// The collection of the Current Blocks for this Document
        /// </summary>
        public BlockCollection Blocks
        {
            get
            {
                return _Blocks;
            }
        }

        /// <summary>
        /// Add a picture from the specified filename 
        /// to the current instance of WordDocument
        /// and return a instance of the Picture class
        /// </summary>
        /// <param name="filename">The source filename of the picture</param>
        /// <returns>Return the Picture Class</returns>
        public Picture AddPicture(string filename)
        {
            Picture pic = null;

            using (FileStream strm = File.Open(filename, FileMode.Open))
            {
                pic = this.AddPicture(strm);
                strm.Close();
            }

            return pic;
        }

                /// <summary>
        /// Add a picture from the specified stream 
        /// to the current instance of WordDocument
        /// and return a instance of the Picture class
        /// </summary>
        /// <param name="stream">The source stream of the picture</param>
        /// <returns>Return the Picture Class</returns>
        public Picture AddPicture(Stream stream)
        {
            byte[] bts = stream.GetAllBytes();
            ImagePart im = _MainDocumentPart.AddImagePart(ImagePartType.Png);
      
            im.FeedData(new MemoryStream(bts));           

            var newPic = new Picture(_MainDocumentPart.GetIdOfPart(im));

            BitmapImage bitmap = new BitmapImage();
            bitmap.CreateOptions = BitmapCreateOptions.PreservePixelFormat;
            bitmap.BeginInit();
            bitmap.StreamSource = new MemoryStream(bts);
            bitmap.EndInit();
            

            newPic.Width = bitmap.PixelWidth.PixelToEmus();
            newPic.Height = bitmap.PixelHeight.PixelToEmus();

            return newPic;
        }

        /// <summary>
        /// The style section for the document,
        /// wich contains defaults a user custom's styles
        /// for this document
        /// </summary>
        public StyleSection StyleSection
        {
            get;
            set;
        }

        //public Picture GetPicture(string filename)
        //{

        //}

        /// <summary>
        /// Save this document to the current URI
        /// </summary>
        public void Save()
        {
            string docXml = this.ToXml().ToString(SaveOptions.DisableFormatting);

            //_WordDocument.DeletePart(_MainDocumentPart);
            //_MainDocumentPart = _WordDocument.s

            using (Stream stream = _MainDocumentPart.GetStream())
            {
                stream.Position = 0;
                
                byte[] buf = (new UTF8Encoding()).GetBytes(docXml);
                stream.Write(buf, 0, buf.Length);
                stream.SetLength(buf.Length);
            }
            if (this.StyleSection != null)
            {
                using (Stream strm = this._WordDocument.MainDocumentPart.StyleDefinitionsPart.GetStream())
                {
                    string stXml = this.StyleSection.ToXml().ToString(SaveOptions.DisableFormatting);

                    byte[] buf = (new UTF8Encoding()).GetBytes(stXml);
                    strm.Write(buf, 0, buf.Length);
                    strm.SetLength(buf.Length);
                }
            }
            this._WordDocument.Close();            
        }

        /// <summary>
        /// Save the document to the specified file
        /// </summary>
        /// <param name="filename"></param>
        public void Save(string filename)
        {
            //string docXml = this.ToXml().ToString(SaveOptions.DisableFormatting);
           
            using (Stream stream = _MainDocumentPart.GetStream())
            {
                //byte[] buf = (new UTF8Encoding()).GetBytes(docXml);
                //stream.Write(buf, 0, buf.Length);
                XDocument xdoc = (XDocument)this.ToXml();
                using (StreamWriter swr = new StreamWriter(stream))
                {
                    xdoc.Save(swr);
                }
            }
            if(this.StyleSection != null)
            {
                using(Stream strm = this._WordDocument.MainDocumentPart.StyleDefinitionsPart.GetStream())
                {
                    string stXml = this.StyleSection.ToXml().ToString(SaveOptions.DisableFormatting);

                    byte[] buf = (new UTF8Encoding()).GetBytes(stXml);
                    strm.Write(buf, 0, buf.Length);
                    strm.SetLength(buf.Length);
                }
            }
            var part = this._WordDocument.MainDocumentPart.AddNewPart<NumberingDefinitionsPart>();
 
            using (var strm = this._WordDocument.MainDocumentPart.NumberingDefinitionsPart.GetStream())
            {
                string stXml = Resources.Numbering;

                    byte[] buf = (new UTF8Encoding()).GetBytes(stXml);
                    strm.Write(buf, 0, buf.Length);
                    strm.SetLength(buf.Length);
            }
            this._WordDocument.Close();

            File.Copy(this._FileName.LocalPath, filename, true);
        }

        /// <summary>
        /// Close the document and release the main FileStream
        /// </summary>
        public void Close()
        {
            this.Dispose();
        }

        public override System.Xml.Linq.XContainer ToXml()
        {
            //<?xml version="1.0" encoding="UTF-8" standalone="yes"?>

            XDocument xMainDoc = new XDocument(new XDeclaration("1.0", "utf-8", "yes"), null);
            

            XElement xDocument = new XElement(NS.WM + "document",
                new XAttribute(XNamespace.Xmlns + "w", NS.WM.NamespaceName),
                new XAttribute(XNamespace.Xmlns + "r", NS.RS.NamespaceName),
                new XAttribute(XNamespace.Xmlns + "wp", NS.WD.NamespaceName)
                );
            xMainDoc.Add(xDocument);

            XElement xBody = new XElement(NS.WM + "body");

            xDocument.Add(xBody);

            foreach (Block b in this.Blocks)
            {
                xBody.Add(b.ToXml());
            }

            return  xMainDoc;
        }

        #region IDisposable Members

        private bool _disposed = false;

        public void Dispose()
        {
            if (!_disposed)
            {
                _WordDocument.Close();
                _WordDocument.Dispose();
                _disposed = true;
            }
        }

        #endregion

        #region IAddChild Members

        public void AddChild(object value)
        {
            if (!(value is Block))
                throw new Exception("Invalid child type a Block is requireed");

            this._Blocks.Add((Block)value);
        }

        public void AddText(string text)
        {
            throw new NotImplementedException();
        }

        #endregion

        #region IFindable Members

        public OpenXMLEntity FindByName(string name)
        {
            foreach (var b in this.Blocks)
            {
                if (b.Name == name)
                    return b;

                if(b is IFindable)
                {
                    IFindable fb = (IFindable)b;
                    var bs = fb.FindByName(name);

                    if(bs != null)
                        return bs;
                }
            }
            return null;
        }

        #endregion
    }
}
